﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using XZen.Cdn.Client.Blobs;
using System.Net;
using System.Diagnostics;
using System.Runtime.Caching;
using XZen.Cdn.Client;
using XZen.Blogger.Execptions;

namespace XZen.Blogger
{
    public interface IBlogService
    {
        List<Blog> GetBlogs();
        Blog GetBlogByName(string blogName, bool useCache = false);
        Blog GetBlog(long? blogEntryId, bool useCache = false);
        long? CreateBlog(Blog blog);
        Blog EditBlog(Blog blog);
        long? DeleteBlog(Blog blog);
        List<Posting> GetPostings(long? blogId);
        Posting GetPosting(long? postingEntryId);
        long? CreatePosting(long? blogEntryId, Posting posting);
        void EditPosting(Posting posting);
        void PublishPosting(Posting posting);
        long? DeletePosting(Posting posting);
    }
   
    [Serializable]
    public class BlogService:IBlogService
    {
        #region Private Variables & Constructor
        private long? userId;
        private long? subscriptionId;
        private long? rootFolderId;
        private long? blogsFolderId;
        private string user;
        private string subcription;
        private string baseUri;
        private ContentWriter writer;
        MemoryCache _cacheStore = new MemoryCache("BLOG-MAP");
        private int cacheDurationInMinutes = 10;
        private const string BLOGS_FOLDER_NAME = "Blogs";
        private const string COMMENTS_FOLDER_NAME = "Comments";
        private const string RATING_FOLDER_NAME = "Rating";
        private const string POSTING_DOCUMENT_NAME = "index.htm";
        private const string PROPERTY_BAG = "Properties";
        BlobServiceClient Contents;
        public MemoryCache Cache
        {
            get { return _cacheStore; }
            set { _cacheStore = value; }
        }
        public static BlogService _loggedInService ;
        public static string _serviceLock = "cool";

        public BlogService(string userName, string subscriptionName)
        {
            user = userName;
            subcription = subscriptionName;
        }
        #endregion

        #region Book Keeping 
        public static BlogService Instance
        {
            get
            {
                if (_loggedInService == null)
                    throw new LoginException("Not yet initialized, must be initialized and logged in");
                return _loggedInService;
            }
        }
        public static BlogService Create(string userName, string subscriptionName)
        {
            var loggedInService = new BlogService(userName, subscriptionName);
            return loggedInService;
        }
        //public static BlogService Create(string userName, string subscriptionName,ContentSetting settings)
        //{
        //    var loggedInService = new BlogService(userName,subscriptionName);
        //    loggedInService.Login(settings.UserName, settings.Password, settings.BaseUrl);
        //    return loggedInService;
        //}
        public string Login(string userName, string password, string contentUri)
        {
            if (_loggedInService != null)
            {
                Debug.WriteLine("Warning...Already initialized");
            }
            baseUri = contentUri;
            writer = new ContentWriter(contentUri, new ContentSetting(userName, password, contentUri));
            Contents = writer.Client;
            userId = Contents.GetUserByName(user);
            subscriptionId = Contents.GetSubscriptrionByName(userId, subcription);
            rootFolderId = Contents.GetStorageRoot(subscriptionId);
            lock (_serviceLock)
            {
                _loggedInService = this;
                writer.AppSettings.IsInitialized = true;               
            }
            return writer.Token;
        }
        #endregion      

        #region Private Liked Function thus internal
        internal Folder GetBlogsRoot()
        {
            return GetSubFolder(rootFolderId, BLOGS_FOLDER_NAME);
        }   
        internal Folder GetSubFolder(long? folderId, string folderName)
        {
            string key = FolderIDNameKey(folderId,folderName);
            var item = Cache.GetCacheItem(key);
            Folder folder = null;
            if (item == null)
            {
                folder = Contents.FindSubFolder(folderId, folderName);
                if (folder !=null)
                Cache.Set(key, folder, DefaultPolicy());
            }
            else
            {
                folder = (Folder)item.Value;
            }
            return folder;          
        }           
        internal Folder GetFolder(long? folderId)
        {
            string key = FolderIDKey(folderId);
            var item = Cache.GetCacheItem(key);
            Folder folder = null;
            if (item == null)
            {
                folder = Contents.GetFolder(folderId);
                if (folder != null)
                Cache.Set(key, folder, DefaultPolicy());
            }
            else
            {
                folder = (Folder)item.Value;
            }
            return folder;
        }

        /*******************  LOW LEVEL  ************************/
        internal string FolderIDKey(long? folderId)
        {
            return "FolderByID=" + folderId.ToString(); 
        }
        internal string FolderIDNameKey(long? folderId,string folderName)
        {
            return "FolderByID=" + folderId.ToString() + "&FolderByName=" + folderName;
        }
        internal CacheItemPolicy DefaultPolicy()
        {
            CacheItemPolicy policy = new CacheItemPolicy() { AbsoluteExpiration = DateTime.Now.AddMinutes(cacheDurationInMinutes) };
            return policy;
        }
        #endregion
        public List<Blog> GetBlogs()
        {
            Folder blogFolder = GetBlogsRoot();
            if (blogFolder == null)
            {
                //He has not created a blog so far
                return new List<Blog>();
            }
            var subfolder = Contents.ListFolders(blogFolder.FolderID);
            var blogs = (from f in subfolder select ToBlog(f));
            return blogs.ToList();
        }
        public Blog GetBlogByName(string blogName, bool useCache = false)
        {
            Folder blogFolder = GetBlogsRoot();
            //space terminated string
            var folder = (useCache) ? GetSubFolder(blogFolder.FolderID, blogName) : Contents.FindSubFolder(blogFolder.FolderID, blogName);
            var blog = ToBlog(folder);
            return blog;
        }
        public Blog GetBlog(long? blogEntryId, bool useCache = false)
        {
            var folder = (useCache) ? GetFolder(blogEntryId) : Contents.GetFolder(blogEntryId);
            var blog = ToBlog(folder);
            return blog;
        }
        public long? CreateBlog(Blog blog)
        {
            long? blogEntryId;
            // if there is already [Root=>Blogs] folder..
            var blogFolder = GetBlogsRoot();
            if (blogFolder == null)
            {
                blogsFolderId = Contents.CreateSubFolder(rootFolderId, BLOGS_FOLDER_NAME);
            }
            else
            {
                blogsFolderId = blogFolder.FolderID;
            }

            string blogname = blog.Name.Replace(' ', '_'); //remove special characters
            // Lets set up blog folder

            Folder entryFolder = GetSubFolder(blogsFolderId, blogname);
            if (entryFolder == null)
            {
                blogEntryId = Contents.CreateSubFolder(blogsFolderId, blogname);
                var folder = Contents.GetFolder(blogEntryId);
                folder.Caption = blog.Title;
                Contents.RenameFolder(folder);

                // Setting it to 
                var attrs = new Dictionary<string, string>() { 
                        { "ip" ,blog.IP} , 
                        { "category", blog.Category},
                        { "title", blog.Title},
                        {  "author", user},
                        {"description", blog.Body},
                        {"source", subcription},
                        { "site", baseUri}
                };
                Contents.AddFolderAttributes(blogEntryId, attrs);
            }
            else
            {
                throw new ArgumentException("Blog already exists");
            }
            return blogEntryId;
        }
        public Blog EditBlog(Blog blog)
        {
            Folder entryFolder = GetFolder(blog.ToID());
            entryFolder.FolderName = blog.Name;
            entryFolder.Caption = blog.Title;
            entryFolder.Status = (int)FileStatus.Visited;
            entryFolder = Contents.UpdateFolder(entryFolder);
            var attrs = new Dictionary<string, string>() { 
                        {   "ip" ,blog.IP} , 
                        {   "category", blog.Category},
                        {   "title", blog.Title},
                        {  "author", user},
                        {   "description", blog.Body},
                        {   "source", subcription},
                        {   "site", baseUri}
                };
            Contents.AddFolderAttributes(entryFolder.FolderID, attrs);
            Cache.Remove(FolderIDKey(entryFolder.FolderID));
            return ToBlog(entryFolder);
        }
        public long? DeleteBlog(Blog blog)
        {
            Folder entryFolder = GetFolder(blog.ToID());            
            Cache.Remove(FolderIDKey(entryFolder.FolderID));
            Contents.RemoveFolder(entryFolder.FolderID, true);
            return entryFolder.FolderID;
        }

        /// <summary>
        /// BlogID is posting folders container
        /// </summary>
        /// <param name="blogId"></param>
        /// <returns></returns>
        public List<Posting> GetPostings(long? blogId)
        {
            //these are postings
            var subfolder = Contents.ListFolders(blogId);
            var posts = (from f in subfolder select ToPosting(f)).ToList();
            return posts;
        }
        public Posting GetPosting(long? postingEntryId)
        {
            var folder = Contents.GetFolder(postingEntryId);          
            File file = Contents.GetFileByName(folder.FolderID, POSTING_DOCUMENT_NAME);
            var bytes = Contents.GetFileContent(file.FileID);
            var posting = ToPosting(folder);
            posting.Body = UTF8Encoding.UTF8.GetString(bytes);
            return posting;
        }

        public long? CreatePosting(long? blogEntryId, Posting posting)
        {
            string postName = posting.Name.Replace(" ", "_");
            long? postingFolderId;
            //Get the Posting Folder
            Folder postingFolder = GetSubFolder(blogEntryId, postName);
            
            if (postingFolder == null)
            {
                postingFolderId = Contents.CreateSubFolder(blogEntryId, postName);
                postingFolder = GetFolder(postingFolderId);
            }
            else
            {  
                postingFolderId = postingFolder.FolderID;                
            }

            //Create Comments folder
            long? commentsFolderId = Contents.CreateSubFolder(postingFolderId, COMMENTS_FOLDER_NAME);
            long? ratingFolderId = Contents.CreateSubFolder(postingFolderId, RATING_FOLDER_NAME);
            var fileId = Contents.CreateFileWithContent(postingFolderId, POSTING_DOCUMENT_NAME, UTF8Encoding.UTF8.GetBytes(posting.Body), "text/html");

            var commentfileId = Contents.CreateFileWithContent(postingFolderId, PROPERTY_BAG, UTF8Encoding.UTF8.GetBytes("Properties"), "text/html");
            // Setting it to 
            var attrs = new Dictionary<string, string>() { 
                { "id" ,posting.IP} , 
                { "category", posting.Category},
                { "title", posting.Title},
                {  "author", user},
                {"source", subcription},
                { "site", baseUri}
            };
            Contents.AddFolderAttributes(postingFolderId, attrs);
            //Setting it to new..
            postingFolder.Caption = posting.Title;
            Contents.UpdateFolder(postingFolder);
            Contents.SetFolderStatus(postingFolderId, FileStatus.Draft);
            return postingFolderId;
        }
        public void EditPosting(Posting posting)
        {
            Folder postingFolder = GetFolder(posting.ToID());
            if (postingFolder == null)
            {
                throw new ArgumentException("Post already exists");
            }
            File file = Contents.GetFileByName(postingFolder.FolderID, POSTING_DOCUMENT_NAME);
            long? fileId = file.FileID;
            postingFolder.FolderName = posting.Name;
            postingFolder.Caption = posting.Title;
            postingFolder.Status = (int)FileStatus.Draft;
            Contents.UpdateFolder(postingFolder);
            var attrs = new Dictionary<string, string>() { 
                { "id" ,posting.IP} , 
                { "category", posting.Category},
                { "title", posting.Title},
                {  "author", user},
                {"source", subcription},
                { "site", baseUri}
            };
            Contents.AddFolderAttributes(postingFolder.FolderID, attrs);
            // this will have newer version...
            Contents.UploadFileContent(fileId, UTF8Encoding.UTF8.GetBytes(posting.Body), "text/html");
            //Contents.SetFolderStatus(postingFolder.FolderID, FileStatus.Draft);
            Cache.Remove(FolderIDKey(postingFolder.FolderID));
        }
        public void PublishPosting(Posting posting)
        {
            Folder postingFolder = GetFolder(posting.ToID());
            if (postingFolder == null)
            {
                throw new ArgumentException("Post already exists");
            }
            File file = Contents.GetFileByName(postingFolder.FolderID, "index.htm");
            long? fileId = file.FileID;
            Contents.SetFolderStatus(postingFolder.FolderID, FileStatus.Posted);
            Cache.Remove(FolderIDKey(postingFolder.FolderID));
        }
        public long? DeletePosting(Posting posting)
        {
            Folder postingFolder = GetFolder(posting.ToID());            
            Cache.Remove(FolderIDKey(posting.ToID()));
            Contents.RemoveFolder(postingFolder.FolderID, true);
            return postingFolder.FolderID;
        }
       
        public static Posting ToPosting(Folder folder)
        {
            var post = new Posting
            {
                ID = folder.FolderID.ToString(),
                Body = folder.FolderName,
                Category = "Dont Know",
                IP = "dont know",
                Name = folder.FolderName,
                Title = folder.Caption,
                User = folder.OwnerUserName,  
                Status = folder.Status.ToStatus(),
                BlogID = folder.ParentID.ToString(), 
                Comments = new List<Comment>(), 
                Ratings = new List<Rating>()  
            };
            var attrs = folder.Attributes();
            post.Category = attrs.GetString("category");
            post.IP = attrs.GetString("ip");
            return post;
        }
        public static Blog ToBlog(Folder folder)
        {
            //var attrs = folder.Html;
            var blog = new Blog
            {
                ID = folder.FolderID.ToString(),
                Body = folder.FolderName,
                Category = "Dont Know",
                IP = "dont know",
                Name = folder.FolderName,
                Title = folder.Caption,
                User = folder.OwnerUserName
            };
            var attrs = folder.Attributes();
            blog.Category = attrs.GetString("category");
            blog.IP = attrs.GetString("ip");
            blog.Body = attrs.GetString("description");
            return blog;
        }
        public static Folder Apply(Folder folder,Blog blog )
        {
            folder.Caption = blog.Title;
            folder.FolderName = blog.Name;            
            return folder;
        }
        public static Folder Apply(Folder folder, Posting posting)
        {
            folder.Caption = posting.Title;
            folder.FolderName = posting.Name;
            return folder;
        }
    }
}
